package vip.aster.quartz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.aster.common.utils.PageInfo;
import vip.aster.quartz.constant.ScheduleConstants;
import vip.aster.quartz.entity.ScheduleJob;
import vip.aster.quartz.mapper.ScheduleJobMapper;
import vip.aster.quartz.query.ScheduleJobQuery;
import vip.aster.quartz.service.ScheduleJobService;
import vip.aster.quartz.utils.ScheduleUtils;
import vip.aster.quartz.vo.ScheduleJobVO;

import java.util.List;

/**
 * <p>
 * 定时任务调度 服务实现类
 * </P>
 *
 * @author Aster lipian1004@163.com
 * @since 2024-03-06 10:24:20
 */
@Service
@AllArgsConstructor
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements ScheduleJobService {
    private ScheduleJobMapper scheduleJobMapper;
    private Scheduler scheduler;

    @Override
    public PageInfo<ScheduleJobVO> pageList(ScheduleJobQuery query) {
        Page<ScheduleJob> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<ScheduleJob> pageList = scheduleJobMapper.selectPage(page, getWrapper(query));
        return new PageInfo<>(ScheduleJobVO.convertList(pageList.getRecords()), pageList.getTotal());
    }

    @Override
    public void save(ScheduleJobVO vo) throws SchedulerException {
        ScheduleJob entity = vo.reconvert();

        if (StrUtil.isNotBlank(entity.getId())) {
            scheduleJobMapper.updateById(entity);
            updateSchedulerJob(entity, entity.getJobGroup());
        } else {
            int rows = scheduleJobMapper.insert(entity);
            if (rows > 0) {
                ScheduleUtils.createScheduleJob(scheduler, entity);
            }
        }
    }

    /**
     * 更新任务
     *
     * @param job      任务对象
     * @param jobGroup 任务组名
     */
    private void updateSchedulerJob(ScheduleJob job, String jobGroup) throws SchedulerException {
        String jobId = job.getId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    @Override
    public void run(ScheduleJobVO vo) throws SchedulerException {
        ScheduleJob scheduleJob = this.getById(vo.getId());
        if (scheduleJob == null) {
            return;
        }
        String jobGroup = scheduleJob.getJobGroup();
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, scheduleJob);
        JobKey jobKey = ScheduleUtils.getJobKey(vo.getId(), jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.triggerJob(jobKey, dataMap);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(ScheduleJobVO vo) throws SchedulerException {
        ScheduleJob scheduleJob = this.getById(vo.getId());
        if (scheduleJob == null) {
            return;
        }

        // 更新数据
        scheduleJob.setStatus(vo.getStatus());
        this.updateById(scheduleJob);

        if (ScheduleConstants.Status.NORMAL.getValue().equals(vo.getStatus())) {
            this.resumeJob(scheduleJob);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(vo.getStatus())) {
            this.pauseJob(scheduleJob);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pauseJob(ScheduleJob job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = scheduleJobMapper.updateById(job);
        if (rows > 0) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resumeJob(ScheduleJob job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = scheduleJobMapper.updateById(job);
        if (rows > 0) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    @Override
    public void deleteJobByIds(List<String> jobIds) throws SchedulerException {
        for (String jobId : jobIds) {
            ScheduleJob job = this.getById(jobId);
            deleteJob(job);
        }
    }

    private void deleteJob(ScheduleJob job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        int rows = scheduleJobMapper.deleteById(jobId);
        if (rows > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    private LambdaQueryWrapper<ScheduleJob> getWrapper(ScheduleJobQuery query) {
        LambdaQueryWrapper<ScheduleJob> wrapper = Wrappers.lambdaQuery();
        wrapper.like(StrUtil.isNotBlank(query.getJobName()), ScheduleJob::getJobName, query.getJobName());
        wrapper.like(StrUtil.isNotBlank(query.getJobGroup()), ScheduleJob::getJobGroup, query.getJobGroup());
        wrapper.eq(StrUtil.isNotBlank(query.getStatus()), ScheduleJob::getStatus, query.getStatus());
        return wrapper;
    }

}